/** * TemplateRenderer - Subcomponent which renders a Template by replication. * * Copyright (c) 2002 * Marty Phelan, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package com.taursys.xml.render; import com.taursys.debug.Debug; import com.taursys.xml.event.Dispatcher; import com.taursys.xml.event.RenderEvent; import com.taursys.xml.event.RenderDispatcher; import com.taursys.xml.event.RenderException; import com.taursys.xml.event.RecycleEvent; import com.taursys.xml.event.RecycleDispatcher; import com.taursys.xml.event.RecycleException; import com.taursys.model.CollectionValueHolder; import com.taursys.xml.Component; import com.taursys.xml.Container; import com.taursys.xml.DocumentComponent; import com.taursys.xml.Template; import org.w3c.dom.Node; import org.w3c.dom.Element; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /** * TemplateRenderer is a subcomponent which renders a Template by replication. * @author Marty Phelan * @version 1.0 */ public class TemplateRenderer extends DocumentElementRenderer { private Collection clones = new ArrayList(); /** * Constructs a new TemplateRenderer */ public TemplateRenderer(Template template) { super(template); } /** * Recycles this component by restoring the Document element to a default state. * This implementation simply makes it visible, and dispatches a recycle * event to any children. * <p> * If a Document element was left invisible, and the Document changed, then * the invisible component would become dereferenced. * @throws RecycleException if problem occurs during recycling. */ public void recycle() throws RecycleException { super.recycle(); removeClones(); } /** * Renders the Template and its children to the Document. * This subcomponent uses replication as its rendering strategy. * It begins by removing any cloned node from the Document (from prior use). * If the Template isVisible, it begins by restoring itself back into the * Document (if removed by prior use) and then iterates over the items * in the collectionValue holder. For each item, it invokes the Template's * renderDispatcher so child components will render their values. It then * clones itself, and alters the ID's in the cloned branch by appending a row * number to the ID (example "__row3"). Finally, it removes itself from * the document. * * @throws RenderException if any problem occurs during rendering */ public void render() throws RenderException { init(); removeClones(); if (getComponent().isVisible()) { restoreSelf(); CollectionValueHolder collectionValueHolder = ((Template)getComponent()).getCollectionValueHolder(); if (collectionValueHolder != null) { int i = 1; collectionValueHolder.reset(); RenderDispatcher dispatcher = (RenderDispatcher) ((Container)getComponent()).getDispatcher( RenderEvent.class.getName()); while (collectionValueHolder.hasNext()) { collectionValueHolder.next(); dispatcher.dispatch(); cloneSelf("__row" + i); i++; } } else { Debug.warn("Template with id=" + ((Template)getComponent()).getId() + " has a null CollectionValueHolder during rendering."); } } removeSelf(); } /** * Remove all clones from parentNode and clear clones Collection. */ protected void removeClones() { // Remove all cloned rows from parent Iterator i = clones.iterator(); while(i.hasNext()) getParentNode().removeChild((Node)i.next()); // Clear out clones clones.clear(); } /** * Populate children of this node and create a clone of this node. * Also invokes alterIDs with given appendValue to avoid duplicate ID's * (violation of XML specification). * @param appendValue to append to ID's to avoid duplicates during cloning */ protected void cloneSelf(String appendValue) { // Clone the componentNode Node cloneNode = getComponentNode().cloneNode(true); // Remove all Id's from branch alterIDs((Element)cloneNode, appendValue); // Insert this new node BEFORE the this node (keeps original order) getParentNode().insertBefore(cloneNode, getComponentNode()); // Save reference to cloned node for removal clones.add(cloneNode); } /** * Alter ID's for this node and its children recursively to avoid duplicates. * @param the beginning parent node for the operation. * @param the String to append to each nodes existing id */ protected void alterIDs(Element parent, String appendValue) { // First do parent String id = parent.getAttribute("id"); if (id != null && id.length() > 0) { id += appendValue; parent.setAttribute("id", id); } // Then do children Node child = parent.getFirstChild(); while (child != null) { if (child.getNodeType() == Node.ELEMENT_NODE) { // recurse node alterIDs((Element)child, appendValue); } // next node child = child.getNextSibling(); } } /** * Get the collection of cloned nodes which were generated by the render method. * @return the collection of cloned nodes which were generated by the render method. */ public Collection getClones() { return clones; } }